/*
 * generate ccd file, by edhong
 * Jan-March 2008
 *
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include "imgccd.h"
#include "SubbandBits.h"
#include "Characteristics.h"

#define USG_STRING "[-b %d:numbitplanes=10] [-nl %d:waveletlevels=(enough to make LL have side of length <=8)] [-ct %d:characteristictype=1] [-s %d:sortingMethod=0] [-sc %d:sortingCharacteristictype] [-cd %s:sortctdfilename] [-cl %d:colortype=2] [-cct %d:colorcharacteristictype=1] [-d %d:debugflag=0] [-out %d:outputopt=0; 1=ccd only; 2 = cc only on stdout] %s:imgfile %s:outfilebasename"

QccWAVWavelet Wavelet;
QccString WaveletFilename = QCCWAVWAVELET_DEFAULT_WAVELET;
QccString Boundary = "symmetric";
QccString outfilebasename = "outputfile";
QccString ccdfilename, ccfilename, sortctdfilename; 

int NumLevels = -1;
int NumSubbands;
int NumBitplanes = 10;
int CharacteristicType = 1;
int debugflag = 0;
int colortype = QCCIMGCOLORTYPE_YCbCr;
int ColorCharacteristicType =1;
int outputopt = 0; 

QccIMGImage InputImage;
QccWAVSubbandPyramid SubbandPyramid[3];

int ImageNumCols;
int ImageNumRows;
int numComponents;
int sortNum = 0;
int sortCharacteristictype = -1; 

int main(int argc, char *argv[])
{
  
  QccInit(argc, argv);

  QccWAVWaveletInitialize(&Wavelet);
  QccIMGImageInitialize(&InputImage);
  QccWAVSubbandPyramidInitialize(&SubbandPyramid[0]);
 QccWAVSubbandPyramidInitialize(&SubbandPyramid[1]);
 QccWAVSubbandPyramidInitialize(&SubbandPyramid[2]);
  
  if (QccParseParameters(argc, argv,
                         USG_STRING,
                         &NumBitplanes,
                         &NumLevels,
			 &CharacteristicType, 
			 &sortNum,
			 &sortCharacteristictype,
			 sortctdfilename,
                         &colortype,
                         &ColorCharacteristicType,
			 &debugflag,
                         &outputopt, 
                         InputImage.filename,
                         &outfilebasename))
    QccErrorExit();

  if (NumBitplanes < 1)
    {
      QccErrorAddMessage("%s: Number of bitplanes must be 1 or greater",
                         argv[0]);
      QccErrorExit();
    }
     
  unsigned long long flags[2];
  unsigned long long sortingflags[2];
  
  if (QccWAVWaveletCreate(&Wavelet, WaveletFilename, Boundary))
    {
      QccErrorAddMessage("%s: Error calling QccWAVWaveletCreate()",
                         argv[0]);
      QccErrorExit();
    }
  
  if  (QccIMGImageSetColorType(&InputImage, colortype)) 
    {
      QccErrorAddMessage("%s: Error calling QccIMGImageSetColorType()",
                         argv[0]);
      QccErrorExit();
    
    }

  if (QccIMGImageRead(&InputImage))
    {
      QccErrorAddMessage("%s: Error calling QccIMGImageRead()",
                         argv[0]);
      QccErrorExit();
    }


  ImageNumCols = InputImage.Y.num_cols;
  ImageNumRows = InputImage.Y.num_rows;


  int temp = computeNumWaveletLevels(ImageNumRows, ImageNumCols);
  if (NumLevels == -1) {
    NumLevels = temp;
  }
  else if (NumLevels < -1)
    {
      QccErrorAddMessage("%s: Number of levels must be non-negative",
                         argv[0]);
      QccErrorExit();
    }
  else if (NumLevels > temp){
    QccErrorAddMessage("%s: Image too small to use %d wavelet levels",
		       argv[0], NumLevels);
    QccErrorExit();    
  }

  NumSubbands = QccWAVSubbandPyramidNumLevelsToNumSubbands(NumLevels);

  InitAllC(NumSubbands, NumBitplanes);

  if (getCharacteristicSet(CharacteristicType, flags ))
    {
      QccErrorExit();
    }
    
  if (QccIMGImageColor(&InputImage)) {
    if (addColorCharacteristicSet(ColorCharacteristicType, flags))
    {
        QccErrorExit();
    }
  }


  if (sortCharacteristictype == -1) {
    sortCharacteristictype = CharacteristicType;
  }
  if (getCharacteristicSet(sortCharacteristictype, sortingflags ))
    {
      QccErrorExit();
    }
  

  numComponents = 1;
  SubbandPyramid[0].num_levels = 0;
  SubbandPyramid[0].num_rows = ImageNumRows;
  SubbandPyramid[0].num_cols = ImageNumCols;
  SubbandPyramid[0].matrix = InputImage.Y.image;

  if (QccIMGImageColor(&InputImage))
    {
      numComponents = 3;
      SubbandPyramid[1] = SubbandPyramid[0];
      SubbandPyramid[2] = SubbandPyramid[0];
      SubbandPyramid[1].matrix = InputImage.U.image;
      SubbandPyramid[2].matrix = InputImage.V.image;
    }

  int i,j, k;
    // subtract mean value of possible 8-bit values. 
  for (i=0; i < SubbandPyramid[0].num_rows; i++) {
    for (j=0; j < SubbandPyramid[0].num_cols; j++) 
       SubbandPyramid[0].matrix[i][j] -= (255.0/2);
  }
    // make chrominance bands (if any) range from -127.5 to 127.5
    for (k=1; k < numComponents; k++) 
      for (i=0; i < SubbandPyramid[k].num_rows; i++) {
        for (j=0; j < SubbandPyramid[k].num_cols; j++) 
            switch (colortype) {
                case  QCCIMGCOLORTYPE_YCbCr:
                case QCCIMGCOLORTYPE_YUV:
                // do nothing; ranges already correct. 
                   break;
                case QCCIMGCOLORTYPE_RGB:
                    SubbandPyramid[k].matrix[i][j] -= (255.0/2);
                   break;
                default: 
                   fprintf(stderr, "Unimplemented color type %d\n", colortype);                   
            }
      } 
  
  
  for (i=0; i < numComponents; i++) {

   if (QccWAVSubbandPyramidDWT(&SubbandPyramid[i],
			      NumLevels,
			      &Wavelet))
     {
      QccErrorAddMessage("%s: Error calling QccWAVSubbandPyramidDWT()",
			 argv[0]);
      QccErrorExit();
     }
  }


  QccStringSprintf(ccdfilename, "%s.ccd", outfilebasename);
  QccStringSprintf(ccfilename, "%s.cc", outfilebasename);

  if((sortctdfilename == NULL || sortctdfilename[0]=='\0') && 
     sortNum !=0) {
      QccErrorAddMessage("%s: to sort, must specify both valid sorting function and sortctdfilename",
			 argv[0]);
      QccErrorExit();    
  }

  if (outputopt!=2) {  
    printf("Reading image %s, writing to %s and %s\n", InputImage.filename, ccdfilename, ccfilename);
    printf("  parameters: %d bit-planes, %d wavelet levels, Characteristic type=%d, debug=%d\n",
	 NumBitplanes, NumLevels, CharacteristicType, debugflag);
    printf("  parameters: colortype %d, colorCharacteristicType %d\n", colortype, 
        ColorCharacteristicType);
    printf("  parameters: outputoption %d\n", outputopt);
    if (sortNum !=0) 
      printf("  sortnum = %d, sorttype = %d, sortctdfilename = %s\n", 
	   sortNum, sortCharacteristictype, sortctdfilename);
  }
  else if (outputopt==2){
    QccStringSprintf(ccfilename, "-");
  }
  
  if (outputopt==0 || outputopt==1)
    if (WriteCCD(ccdfilename, flags, NumBitplanes)) {
      QccErrorAddMessage("%s: Error Calling WriteCCD",
		       argv[0]);
      QccErrorExit();        
    }

    
  if (outputopt == 0 || outputopt == 2) 
    if( WriteCC(ccfilename, SubbandPyramid, numComponents,
	      NumBitplanes, flags, 
	      get_sorting_function(sortNum),
	      sortingflags, 
	      sortctdfilename,
	      debugflag)) {
      QccErrorAddMessage("%s: Error Calling WriteCC",
		       argv[0]);
      QccErrorExit();        
      }

  QccWAVWaveletFree(&Wavelet);
  QccIMGImageFree(&InputImage);

  QccExit;
}
